package store.aixx.jwt.demo.security.filter;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import store.aixx.jwt.demo.constant.AuthConstant;
import store.aixx.jwt.demo.util.CollectionUtil;
import store.aixx.jwt.demo.util.JwtUtil;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @author yukai
 * @since 2021年12月10日 23:34
 */
@Component
public class JwtFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (checkJwtToken(request)){
            // 先移除认证信息
            SecurityContextHolder.clearContext();
            // 如果有值更新认证信息
            validateToken(request).filter(claims -> claims.get("authorities") != null).ifPresent(setupSpringAuthentication());
        }
        filterChain.doFilter(request, response);
    }

    private Consumer<Claims> setupSpringAuthentication() {
        return claims -> {
            List<?> rawList = CollectionUtil.convertObjectToList(claims.get("authorities"));
            List<SimpleGrantedAuthority> authorities = rawList.stream().map(String::valueOf).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
            SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(claims.getSubject(), null, authorities));
        };
    }

    /**
     * 检查 Jwt Token 是否在 request header中
     *
     * @param request HTTP 请求
     * @return 是否有 JWT Token
     */
    private boolean checkJwtToken(HttpServletRequest request) {
        String tokenHeader = request.getHeader(AuthConstant.TOKEN_HEADER_NAME);

        return StringUtils.hasText(tokenHeader) && tokenHeader.startsWith(AuthConstant.TOKEN_PREFIX);
    }

    private Optional<Claims> validateToken(HttpServletRequest request){
        String token = request.getHeader(AuthConstant.TOKEN_HEADER_NAME).replace(AuthConstant.TOKEN_PREFIX, "");
        try {
            return Optional.of(Jwts.parserBuilder().setSigningKey(JwtUtil.ACCESS_KEY).build().parseClaimsJws(token).getBody());
        }catch ( ExpiredJwtException |SignatureException| MalformedJwtException| UnsupportedJwtException| IllegalArgumentException e){
            return Optional.empty();
        }
    }
}
